/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict'

var FLOWABLE = FLOWABLE || {}
FLOWABLE.TOOLBAR = {
  ACTIONS: {
    saveModel: function(services) {
      _internalCreateModal(
        {
          backdrop: true,
          keyboard: true,
          template: 'editor-app/popups/save-model.html?version=' + Date.now(),
          scope: services.$scope
        },
        services.$modal,
        services.$scope
      )
    },

    validate: function(services) {
      _internalCreateModal(
        {
          backdrop: true,
          keyboard: true,
          template: 'editor-app/popups/validate-model.html?version=' + Date.now(),
          scope: services.$scope
        },
        services.$modal,
        services.$scope
      )
    },

    undo: function(services) {
      // Get the last commands
      var lastCommands = services.$scope.undoStack.pop()

      if (lastCommands) {
        // Add the commands to the redo stack
        services.$scope.redoStack.push(lastCommands)

        // Force refresh of selection, might be that the undo command
        // impacts properties in the selected item
        if (services.$rootScope && services.$rootScope.forceSelectionRefresh) {
          services.$rootScope.forceSelectionRefresh = true
        }

        // Rollback every command
        for (var i = lastCommands.length - 1; i >= 0; --i) {
          lastCommands[i].rollback()
        }

        // Update and refresh the canvas
        services.editorManager.handleEvents({
          type: ORYX.CONFIG.EVENT_UNDO_ROLLBACK,
          commands: lastCommands
        })

        // Update
        services.editorManager.getCanvas().update()
        services.editorManager.updateSelection()
      }

      var toggleUndo = false
      if (services.$scope.undoStack.length == 0) {
        toggleUndo = true
      }

      var toggleRedo = false
      if (services.$scope.redoStack.length > 0) {
        toggleRedo = true
      }

      if (toggleUndo || toggleRedo) {
        for (var i = 0; i < services.$scope.items.length; i++) {
          var item = services.$scope.items[i]
          if (toggleUndo && item.action === 'FLOWABLE.TOOLBAR.ACTIONS.undo') {
            services.$scope.safeApply(function() {
              item.enabled = false
            })
          } else if (toggleRedo && item.action === 'FLOWABLE.TOOLBAR.ACTIONS.redo') {
            services.$scope.safeApply(function() {
              item.enabled = true
            })
          }
        }
      }
    },

    redo: function(services) {
      // Get the last commands from the redo stack
      var lastCommands = services.$scope.redoStack.pop()

      if (lastCommands) {
        // Add this commands to the undo stack
        services.$scope.undoStack.push(lastCommands)

        // Force refresh of selection, might be that the redo command
        // impacts properties in the selected item
        if (services.$rootScope && services.$rootScope.forceSelectionRefresh) {
          services.$rootScope.forceSelectionRefresh = true
        }

        // Execute those commands
        lastCommands.each(function(command) {
          command.execute()
        })

        // Update and refresh the canvas
        services.editorManager.handleEvents({
          type: ORYX.CONFIG.EVENT_UNDO_EXECUTE,
          commands: lastCommands
        })

        // Update
        services.editorManager.getCanvas().update()
        services.editorManager.updateSelection()
      }

      var toggleUndo = false
      if (services.$scope.undoStack.length > 0) {
        toggleUndo = true
      }

      var toggleRedo = false
      if (services.$scope.redoStack.length == 0) {
        toggleRedo = true
      }

      if (toggleUndo || toggleRedo) {
        for (var i = 0; i < services.$scope.items.length; i++) {
          var item = services.$scope.items[i]
          if (toggleUndo && item.action === 'FLOWABLE.TOOLBAR.ACTIONS.undo') {
            services.$scope.safeApply(function() {
              item.enabled = true
            })
          } else if (toggleRedo && item.action === 'FLOWABLE.TOOLBAR.ACTIONS.redo') {
            services.$scope.safeApply(function() {
              item.enabled = false
            })
          }
        }
      }
    },

    cut: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxEditPlugin(services).editCut()
      for (var i = 0; i < services.$scope.items.length; i++) {
        var item = services.$scope.items[i]
        if (item.action === 'FLOWABLE.TOOLBAR.ACTIONS.paste') {
          services.$scope.safeApply(function() {
            item.enabled = true
          })
        }
      }
    },

    copy: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxEditPlugin(services).editCopy()
      for (var i = 0; i < services.$scope.items.length; i++) {
        var item = services.$scope.items[i]
        if (item.action === 'FLOWABLE.TOOLBAR.ACTIONS.paste') {
          services.$scope.safeApply(function() {
            item.enabled = true
          })
        }
      }
    },

    paste: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxEditPlugin(services).editPaste()
    },

    deleteItem: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxEditPlugin(services).editDelete()
    },

    addBendPoint: function(services) {
      // Show the tutorial the first time
      FLOWABLE_EDITOR_TOUR.sequenceFlowBendpoint(services.$scope, services.$translate, services.$q, true)

      var dockerPlugin = FLOWABLE.TOOLBAR.ACTIONS._getOryxDockerPlugin(services)

      var enableAdd = !dockerPlugin.enabledAdd()
      dockerPlugin.setEnableAdd(enableAdd)
      if (enableAdd) {
        dockerPlugin.setEnableRemove(false)
        document.body.style.cursor = 'pointer'
      } else {
        document.body.style.cursor = 'default'
      }
    },

    removeBendPoint: function(services) {
      // Show the tutorial the first time
      FLOWABLE_EDITOR_TOUR.sequenceFlowBendpoint(services.$scope, services.$translate, services.$q, true)

      var dockerPlugin = FLOWABLE.TOOLBAR.ACTIONS._getOryxDockerPlugin(services)

      var enableRemove = !dockerPlugin.enabledRemove()
      dockerPlugin.setEnableRemove(enableRemove)
      if (enableRemove) {
        dockerPlugin.setEnableAdd(false)
        document.body.style.cursor = 'pointer'
      } else {
        document.body.style.cursor = 'default'
      }
    },

    /**
     * Helper method: fetches the Oryx Edit plugin from the provided scope,
     * if not on the scope, it is created and put on the scope for further use.
     *
     * It's important to reuse the same EditPlugin while the same scope is active,
     * as the clipboard is stored for the whole lifetime of the scope.
     */
    _getOryxEditPlugin: function(services) {
      var $scope = services.$scope
      var editorManager = services.editorManager
      if ($scope.oryxEditPlugin === undefined || $scope.oryxEditPlugin === null) {
        $scope.oryxEditPlugin = new ORYX.Plugins.Edit(editorManager.getEditor())
      }
      return $scope.oryxEditPlugin
    },

    zoomIn: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxViewPlugin(services).zoom([1.0 + ORYX.CONFIG.ZOOM_OFFSET])
    },

    zoomOut: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxViewPlugin(services).zoom([1.0 - ORYX.CONFIG.ZOOM_OFFSET])
    },

    zoomActual: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxViewPlugin(services).setAFixZoomLevel(1)
    },

    zoomFit: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxViewPlugin(services).zoomFitToModel()
    },

    alignVertical: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxArrangmentPlugin(services).alignShapes([ORYX.CONFIG.EDITOR_ALIGN_CENTER])
    },

    alignHorizontal: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxArrangmentPlugin(services).alignShapes([ORYX.CONFIG.EDITOR_ALIGN_MIDDLE])
    },

    sameSize: function(services) {
      FLOWABLE.TOOLBAR.ACTIONS._getOryxArrangmentPlugin(services).alignShapes([ORYX.CONFIG.EDITOR_ALIGN_SIZE])
    },

    help: function(services) {
      FLOWABLE_EDITOR_TOUR.gettingStarted(services.$scope, services.$translate, services.$q)
    },

    /**
     * Helper method: fetches the Oryx View plugin from the provided scope,
     * if not on the scope, it is created and put on the scope for further use.
     */
    _getOryxViewPlugin: function(services) {
      var $scope = services.$scope
      var editorManager = services.editorManager
      if ($scope.oryxViewPlugin === undefined || $scope.oryxViewPlugin === null) {
        $scope.oryxViewPlugin = new ORYX.Plugins.View(editorManager.getEditor())
      }
      return $scope.oryxViewPlugin
    },

    _getOryxArrangmentPlugin: function(services) {
      var $scope = services.$scope
      var editorManager = services.editorManager
      if ($scope.oryxArrangmentPlugin === undefined || $scope.oryxArrangmentPlugin === null) {
        $scope.oryxArrangmentPlugin = new ORYX.Plugins.Arrangement(editorManager.getEditor())
      }
      return $scope.oryxArrangmentPlugin
    },

    _getOryxDockerPlugin: function(services) {
      var $scope = services.$scope
      var editorManager = services.editorManager
      if ($scope.oryxDockerPlugin === undefined || $scope.oryxDockerPlugin === null) {
        $scope.oryxDockerPlugin = new ORYX.Plugins.AddDocker(editorManager.getEditor())
      }
      return $scope.oryxDockerPlugin
    }
  }
}

/** Custom controller for the save dialog */
angular.module('flowableModeler').controller('SaveModelCtrl', [
  '$rootScope',
  '$scope',
  '$http',
  '$route',
  '$location',
  'editorManager',
  function($rootScope, $scope, $http, $route, $location, editorManager) {
    if (editorManager.getCurrentModelId() != editorManager.getModelId()) {
      editorManager.edit(editorManager.getModelId())
    }

    var modelMetaData = editorManager.getBaseModelData()

    var description = ''
    if (modelMetaData.description) {
      description = modelMetaData.description
    }

    var saveDialog = {
      name: modelMetaData.name,
      key: modelMetaData.key,
      description: description,
      newVersion: false,
      comment: ''
    }

    $scope.saveDialog = saveDialog

    $scope.status = {
      loading: false
    }

    $scope.close = function() {
      $scope.$hide()
    }

    $scope.saveAndClose = function() {
      $scope.save(function() {
        if (editorManager.getStencilData()) {
          var stencilNameSpace = editorManager.getStencilData().namespace
          if (
            stencilNameSpace !== undefined &&
            stencilNameSpace !== null &&
            stencilNameSpace.indexOf('cmmn1.1') !== -1
          ) {
            $location.path('/casemodels')
            return
          }
        }

        window.$callParent('CloseModelWindow')
        // $location.path('/processes')
      })
    }

    $scope.save = function(successCallback) {
      if (
        !$scope.saveDialog.name ||
        $scope.saveDialog.name.length == 0 ||
        !$scope.saveDialog.key ||
        $scope.saveDialog.key.length == 0
      ) {
        return
      }

      // Indicator spinner image
      $scope.status = {
        loading: true
      }

      modelMetaData.name = $scope.saveDialog.name
      modelMetaData.key = $scope.saveDialog.key
      modelMetaData.description = $scope.saveDialog.description

      var json = editorManager.getModel()

      var params = {
        modeltype: modelMetaData.model.modelType,
        json_xml: JSON.stringify(json),
        name: $scope.saveDialog.name,
        key: $scope.saveDialog.key,
        description: $scope.saveDialog.description,
        newversion: $scope.saveDialog.newVersion,
        comment: $scope.saveDialog.comment,
        lastUpdated: modelMetaData.lastUpdated
      }

      if ($scope.error && $scope.error.isConflict) {
        params.conflictResolveAction = $scope.error.conflictResolveAction
        if ($scope.error.conflictResolveAction === 'saveAs') {
          params.saveAs = $scope.error.saveAs
        }
      }

      // Update
      $http({
        method: 'POST',
        data: params,
        ignoreErrors: true,
        headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
        transformRequest: function(obj) {
          var str = []
          for (var p in obj) {
            str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]))
          }
          return str.join('&')
        },
        url: FLOWABLE.URL.putModel(modelMetaData.modelId)
      })
        .success(function(data, status, headers, config) {
          editorManager.handleEvents({
            type: ORYX.CONFIG.EVENT_SAVED
          })
          $scope.modelData.name = $scope.saveDialog.name
          $scope.modelData.key = $scope.saveDialog.key
          $scope.modelData.lastUpdated = data.lastUpdated

          $scope.status.loading = false
          $scope.$hide()

          // Fire event to all who is listening
          var saveEvent = {
            type: FLOWABLE.eventBus.EVENT_TYPE_MODEL_SAVED,
            model: params,
            modelId: modelMetaData.modelId,
            eventType: 'update-model'
          }
          FLOWABLE.eventBus.dispatch(FLOWABLE.eventBus.EVENT_TYPE_MODEL_SAVED, saveEvent)

          // Reset state
          $scope.error = undefined
          $scope.status.loading = false

          // Execute any callback
          if (successCallback) {
            successCallback()
          }
        })
        .error(function(data, status, headers, config) {
          if (status == 409) {
            $scope.error = {}
            $scope.error.isConflict = true
            $scope.error.userFullName = data.customData.userFullName
            $scope.error.isNewVersionAllowed = data.customData.newVersionAllowed
            $scope.error.saveAs = modelMetaData.name + '_2'
          } else {
            $scope.error = undefined
            $scope.saveDialog.errorMessage = data.message
          }
          $scope.status.loading = false
        })
    }

    $scope.isOkButtonDisabled = function() {
      if ($scope.status.loading) {
        return false
      } else if ($scope.error && $scope.error.conflictResolveAction) {
        if ($scope.error.conflictResolveAction === 'saveAs') {
          return !$scope.error.saveAs || $scope.error.saveAs.length == 0
        } else {
          return false
        }
      }
      return true
    }

    $scope.okClicked = function() {
      if ($scope.error) {
        if ($scope.error.conflictResolveAction === 'discardChanges') {
          $scope.close()
          $route.reload()
        } else if (
          $scope.error.conflictResolveAction === 'overwrite' ||
          $scope.error.conflictResolveAction === 'newVersion'
        ) {
          $scope.save()
        } else if ($scope.error.conflictResolveAction === 'saveAs') {
          $scope.save(function() {
            $rootScope.ignoreChanges = true // Otherwise will get pop up that changes are not saved.
            if (editorManager.getStencilData()) {
              var stencilNameSpace = editorManager.getStencilData().namespace
              if (
                stencilNameSpace !== undefined &&
                stencilNameSpace !== null &&
                stencilNameSpace.indexOf('cmmn1.1') !== -1
              ) {
                $location.path('/casemodels')
                return
              }
            }
            $location.path('/processes')
          })
        }
      }
    }
  }
])

angular.module('flowableModeler').controller('ValidateModelCtrl', [
  '$scope',
  '$http',
  'editorManager',
  function($scope, $http, editorManager) {
    var editor = editorManager.getEditor()
    var model = editorManager.getModel()

    $scope.status = {
      loading: true
    }

    $scope.model = {
      errors: []
    }

    $scope.errorGrid = {
      data: $scope.model.errors,
      headerRowHeight: 28,
      enableRowSelection: true,
      enableRowHeaderSelection: false,
      multiSelect: false,
      modifierKeysToMultiSelect: false,
      enableHorizontalScrollbar: 0,
      enableColumnMenus: false,
      enableSorting: false,
      columnDefs: [
        { field: 'activityName', displayName: 'Name', width: 125 },
        { field: 'defaultDescription', displayName: 'Description' },
        {
          field: 'warning',
          displayName: 'Critical',
          cellTemplate: 'editor-app/configuration/properties/errorgrid-critical.html',
          width: 100
        }
      ]
    }

    $scope.errorGrid.onRegisterApi = function(gridApi) {
      //set gridApi on scope
      $scope.gridApi = gridApi
      gridApi.selection.on.rowSelectionChanged($scope, function(row) {
        if (row.isSelected) {
          editorManager.navigateTo(row.entity.activityId)
          $scope.$hide()
        }
      })
    }

    $http({
      url: FLOWABLE.URL.validateModel(),
      method: 'POST',
      cache: false,
      headers: {
        'Content-Type': 'application/json;charset=utf-8'
      },
      data: model
    }).then(
      function(response) {
        $scope.status.loading = false
        response.data.forEach(function(row) {
          $scope.model.errors.push(row)
        })
      },
      function(response) {
        console.log(response)
      }
    )
  }
])
